package io.kimmking.dubbo.demo.consumer.service.impl;

import io.kimmking.dubbo.demo.api.AccountProvider;
import io.kimmking.dubbo.demo.api.UserService;
import io.kimmking.dubbo.demo.api.exception.DemoError;
import io.kimmking.dubbo.demo.api.exception.DemoException;
import io.kimmking.dubbo.demo.api.request.ExchangeRequest;
import io.kimmking.dubbo.demo.consumer.mapper.DollarAccountMapper;
import io.kimmking.dubbo.demo.consumer.mapper.RmbAccountMapper;
import io.kimmking.dubbo.demo.consumer.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.hmily.annotation.HmilyTCC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * @author gechenpeng
 * @date 2021-06-07
 */
@Service
@Slf4j
public class AccountServiceImpl implements AccountService {

    private final RmbAccountMapper rmbAccountMapper;

    private final DollarAccountMapper dollarAccountMapper;

    @DubboReference(version = "1.0.0", url = "dubbo://127.0.0.1:12345")
    private AccountProvider accountProvider;

    @DubboReference(version = "1.0.0", url = "dubbo://127.0.0.1:12345")
    private UserService userService;

    @Autowired(required = false)
    public AccountServiceImpl(RmbAccountMapper rmbAccountMapper, DollarAccountMapper dollarAccountMapper) {
        this.rmbAccountMapper = rmbAccountMapper;
        this.dollarAccountMapper = dollarAccountMapper;
    }

    @Override
    @HmilyTCC(confirmMethod = "confirmExchangeForRmb", cancelMethod = "cancelExchangeForRmb")
    public void exchangeForRmb(String userId, BigDecimal dollarAmount) {
        log.info("a start to try");
        int row = dollarAccountMapper.freezeAmount(userId, dollarAmount);
        if (row != 1) {
            throw new DemoException(DemoError.BUSINESS_ERROR);
        }

        log.info("a start to invoke b");
        accountProvider.exchangeForRmb(ExchangeRequest.builder()
                .userId(userId)
                .rmbAmount(dollarAmount.multiply(BigDecimal.valueOf(7)))
                .build());
        log.info("a invoked b");

        log.info("a start to invoke b which throws exception");
        userService.findById(1);

    }

    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public void confirmExchangeForRmb(String userId, BigDecimal dollarAmount) {
        log.info("a start to confirm");
        int row = dollarAccountMapper.deductFromFrozen(userId, dollarAmount);
        if (row != 1) {
            throw new DemoException(DemoError.BUSINESS_ERROR);
        }

        rmbAccountMapper.increaseAmount(userId, dollarAmount.multiply(BigDecimal.valueOf(7)));
    }

    public void cancelExchangeForRmb(String userId, BigDecimal dollarAmount) {
        log.info("a start to cancel");
        int row = dollarAccountMapper.unfreezeAmount(userId, dollarAmount);
        if (row != 1) {
            throw new DemoException(DemoError.BUSINESS_ERROR);
        }
    }
}
